package cn.nawang.ebeim.test.transfer;

import cn.nawang.ebeim.test.service.TestData;
import io.netty.channel.*;
import io.netty.handler.codec.http.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.activation.MimetypesFileTypeMap;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.URI;
import java.net.URISyntaxException;

import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE;

public class TransferUploadHandler extends SimpleChannelInboundHandler<HttpObject> {

    private static final Logger LOG = LoggerFactory.getLogger(TransferUploadHandler.class);

    private FileInfo info;

    private int flag = 0;// 如果为0：未建立连接；如果为1：建立成功;如果为2：传输失败；如果为3：传输成功

    private Long preLength = 0l;

    private String errorMsg = "" ;

    private boolean isConnect ;

    private TransferCallback callback ;

    public TransferUploadHandler(FileInfo info, TransferCallback callback) {
        this.info = info;
        this.callback = callback ;
    }

    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        if(!isConnect()){
            callback.onTransferFailed(info,new TransferException("网络信号差,无法建立连接!"));
        }
        if(flag==3){
            callback.onTransferSuccess(info);
        }else {
            callback.onTransferFailed(info,new TransferException("文件上传失败!" + errorMsg));
        }
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        isConnect = true ;
        if(info.isBreakPointUpload()){
            breakPointUpload(ctx);
        }else {
            upload(ctx);
        }
    }

    private void breakPointUpload(ChannelHandlerContext ctx) throws URISyntaxException {
        File file = new File(info.getPath());
        QueryStringEncoder encoder = new QueryStringEncoder("upload");
        encoder.addParam("type", String.valueOf(info.getType()));
        encoder.addParam("version", info.getVersion());
        encoder.addParam("dataId", info.getDataId());
        encoder.addParam("signature", info.getSignature());
        encoder.addParam("isBreak", "true");


        URI uriGet = new URI(encoder.toString());
        HttpRequest request =
                new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uriGet.toASCIIString());
        HttpHeaders.setContentLength(request, file.length());

        ctx.writeAndFlush(request);
    }


    private void upload(ChannelHandlerContext ctx) throws Exception {
        File file = new File(info.getPath());
        QueryStringEncoder encoder = new QueryStringEncoder("upload");
        encoder.addParam("type", String.valueOf(info.getType()));
        encoder.addParam("version", info.getVersion());
        encoder.addParam("dataId", info.getDataId());
        encoder.addParam("signature", info.getSignature());
        encoder.addParam("isBreak", "false");

        URI uriGet = new URI(encoder.toString());
        HttpRequest request =
                new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uriGet.toASCIIString());
        HttpHeaders.setContentLength(request, file.length());

        MimetypesFileTypeMap mimeTypesMap = new MimetypesFileTypeMap();
        request.headers().set(CONTENT_TYPE, mimeTypesMap.getContentType(file.getPath()));
        if (HttpHeaders.isKeepAlive(request)) {
            request.headers().set("CONNECTION", HttpHeaders.Values.KEEP_ALIVE);
        }

        ctx.write(request);
        uploadFile(ctx,0l);
    }

    private void uploadFile(ChannelHandlerContext ctx, Long uploadLength) throws FileNotFoundException {
        final File file = new File(info.getPath());
        if (!file.exists()) {
            LOG.info("不存在文件：" + file.getAbsolutePath());
            flag = 2;
        } else {
            final RandomAccessFile raf = new RandomAccessFile(file, "r");
            long fileLength = file.length();
            if(uploadLength>0){
                fileLength = fileLength - uploadLength;
                LOG.info("file already exist in server, upload from index: " + uploadLength + ",total transfer:" + fileLength);
            }else {
                LOG.info("upload start ==> dsId: "+ info.getDataId() + ",signature：" + info.getSignature() + ",version: " + info.getVersion() + ",total transfer:" + fileLength);
            }
            ChannelFuture sendFileFuture =
                    ctx.write(new DefaultFileRegion(raf.getChannel(), uploadLength, fileLength),
                    ctx.newProgressivePromise());
            sendFileFuture.addListener(new ChannelProgressiveFutureListener() {
                @Override
                public void operationProgressed(ChannelProgressiveFuture future, long progress,long total) {
                    LOG.debug("progress: " + progress + "/" + total);
                }

                @Override
                public void operationComplete(ChannelProgressiveFuture future) throws Exception {
                    raf.close();
                    if (future.isSuccess()) {
                        flag = 3;
                        LOG.info("upload success: " + info.getPath());
                    } else {
                        flag = 2;
                        LOG.info(future.channel() + "传输到服务端失败");
                        errorMsg = "传输到服务端失败" ;
                    }
                }
            });
            ctx.flush();
        }
    }


    @Override
    protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
        if (msg instanceof HttpResponse) {
            HttpResponse response = (HttpResponse) msg;
            if (HttpResponseStatus.OK.equals(response.getStatus())) {
                if(info.isBreakPointUpload()){
                    long uploadLength = response.headers().getContentLength(response);
                    uploadFile(ctx,uploadLength);
                }
            } else {
                flag = 2;
                errorMsg = "upload failed: " + info.getPath() + ", errorMsg: " + response.headers().get("error");
            }
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("exceptionCaught");
        cause.printStackTrace();
        errorMsg = cause.getMessage();
        ctx.close();
    }

    public int getFlag() {
        return flag;
    }

    public String getErrorMsg() {
        return errorMsg;
    }

    public boolean isConnect() {
        return isConnect;
    }

}
